package com.crashcollection.util.http;

import java.io.ByteArrayOutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.zip.DeflaterOutputStream;

import org.apache.http.protocol.HTTP;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import com.crashcollection.configure.Configure;
import com.crashcollection.lib.CrashInfo;

import android.app.IntentService;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Build;
import android.telephony.TelephonyManager;
import android.text.TextUtils;

public class UploadCrashInfoService extends IntentService {

	
	private static int CRASH_APP_CODE = 0;                                 // 标识崩溃项来自哪个产品，值为0表示测试项目

	private final static String IMEI = "imei";                             // 手机IMEI
	private final static String MANUFACTURER = "manufacturer";             // 手机制造商
	private final static String BRAND = "brand";                           // 手机品牌
	private final static String MODEL = "model";                           // 手机型号
	private final static String APP_CODE = "app_code";                     // 软件标识
                                                                           
	private final static String ANDROID_SDK = "android_sdk";               // android sdk
	private final static String ANDROID_RELEASE = "android_release";       // android release
	private final static String LANGUAGE = "language";                     // 语言
	private final static String APP_VERSION = "app_version";               // 版本
	private final static String NETWORK_TYPE = "network_type";             // 网络类型
	private final static String CRASH_TYPE = "crash_type";                 // 异常类型
	private final static String CRASH_INFO = "crash_info";                 // 异常内容
	private final static String CRASH_TIME = "crash_time";                 // 异常时间
	private final static String CRASH_STACK_MD5 = "crash_stack_md5";       // stack md5 

	public UploadCrashInfoService() {
		super("UploadCrashInfoService");
	}

	public static void setAppCode(int appCode) {
		CRASH_APP_CODE = appCode;
	}

	@Override
	protected void onHandleIntent(Intent intent) {
		if (!isOnline(this)) {
			return; // 网络不可用
		}

		int limit;
		if (isWifiConn(this)) {
			limit = 5;
		} else {
			limit = 1;
		}

		CrashInfoSQLiteHelper sqliteHelper = new CrashInfoSQLiteHelper(this);
		sqliteHelper.deleteUnusedItems();
		
		List<CrashInfo> crashInfoList = sqliteHelper.getCrashInfo(limit);
		while (!crashInfoList.isEmpty()) {
			JSONArray array = new JSONArray(); // 存放要上传到服务端的崩溃信息
			String imei = getDeviceId(this);
			for (CrashInfo crashInfo : crashInfoList) {
				JSONObject obj = crashItem2Json(imei, crashInfo);
				if (obj != null) {
					array.put(obj);
				}
			}

			String rtn = doPost(array.toString(), Configure.Server_url);
			if (TextUtils.isEmpty(rtn)) {
				break;
			} else {
				List<Long> crashTimeList = new ArrayList<Long>(); // 成功上传的崩溃项的主键列表
				try {
					array = new JSONArray(rtn);
					int length = array.length();
					for (int i = 0; i < length; ++i) {
						crashTimeList.add(array.getLong(i));
					}
				} catch (JSONException e) {
					return;
				}

				// 在本地标记已成功上传的崩溃项
				if (crashTimeList.isEmpty()) {
					return;
				} else {
					sqliteHelper.setUploaded(crashTimeList);
				}
			}

			if (isWifiConn(this)) {
				// wifi状态下循环上传崩溃数据直至传完
				crashInfoList = sqliteHelper.getCrashInfo(limit);
			} else {
				// 非wifi状态则不再上传
				break;
			}
		}
	}

	private boolean isOnline(Context context) {
		ConnectivityManager connMgr = (ConnectivityManager) context
				.getSystemService(Context.CONNECTIVITY_SERVICE);
		NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();
		return (networkInfo != null && networkInfo.isConnected());
	}

	private boolean isWifiConn(Context context) {
		ConnectivityManager connMgr = (ConnectivityManager) context
				.getSystemService(Context.CONNECTIVITY_SERVICE);
		NetworkInfo networkInfo = connMgr
				.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
		return networkInfo.isConnected();
	}

	private String getDeviceId(Context context) {
		// 直接从配置文件读取
		SharedPreferences settings = context
				.getSharedPreferences("deviceId", 0);
		String deviceId = settings.getString("deviceId", null);
		if (TextUtils.isEmpty(deviceId)) {
			try {
				TelephonyManager tm = (TelephonyManager) context
						.getSystemService(Context.TELEPHONY_SERVICE);
				deviceId = tm.getDeviceId();
			} catch (Exception e) {

			}
			if (TextUtils.isEmpty(deviceId)) {
				// 有些机器有可能取不到设备号用GUID表示
				deviceId = UUID.randomUUID().toString().replace("-", "");
			}
			// 保存到配置文件(这样可以保证每次都一样)
			SharedPreferences.Editor editor = settings.edit();
			editor.putString("deviceId", deviceId);
			editor.commit();
		}
		return deviceId;
	}

	private JSONObject crashItem2Json(String imei, CrashInfo crashInfo) {
		try {
			JSONObject obj = new JSONObject();

			obj.put(IMEI, imei);
			obj.put(MANUFACTURER, Build.MANUFACTURER);
			obj.put(BRAND, Build.BRAND);
			obj.put(MODEL, Build.MODEL);
			obj.put(APP_CODE, CRASH_APP_CODE);

			obj.put(ANDROID_SDK, crashInfo.getAndroidSdk());
			obj.put(ANDROID_RELEASE, crashInfo.getAndroidRelease());
			obj.put(LANGUAGE, crashInfo.getLanguage());
			obj.put(APP_VERSION, crashInfo.getAppVersion());
			obj.put(NETWORK_TYPE, crashInfo.getNetworkType());
			obj.put(CRASH_TYPE, crashInfo.getCrashType());
			obj.put(CRASH_INFO, crashInfo.getCrashInfo());
			obj.put(CRASH_TIME, crashInfo.getCrashTime());
			
			String md5 = crashInfo.getCrashStackMd5();
			if (md5 == null) {
				md5 = "";
			}
			obj.put(CRASH_STACK_MD5, md5);

			return obj;
		} catch (JSONException e) {
			return null;
		}
	}

	private String doPost(String datas, String uri) {
		datas = compress(datas);

		IHttpRequest req = HttpRequest.getInstance(this);
	    StringBuilder receive = new StringBuilder();
		req.doPost(uri, datas, receive);

		return receive.toString();
	}

	private String compress(String str) {
		if (str == null || str.length() == 0) {
			return str;
		}
		String encode = HTTP.UTF_8;
		try {
			ByteArrayOutputStream out = new ByteArrayOutputStream();
			DeflaterOutputStream dos = new DeflaterOutputStream(out);
			dos.write(str.getBytes(encode));
			dos.close();
			return out.toString(encode);
		} catch (Exception ex) {
			return "";
		}
	}
}
